package com.novunda.platform.config;

import com.novunda.platform.common.repositories.BaseRepositoryImpl;
import com.zaxxer.hikari.HikariDataSource;
import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.StringUtils;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import javax.validation.Validator;
import java.util.Properties;

@Configuration
@ComponentScan(basePackages = {"com.novunda.platform"}, excludeFilters = {
//        @ComponentScan.Filter(pattern = "com.novunda.platform.web.*", type = FilterType.REGEX)
})
@EnableAspectJAutoProxy
@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryImpl.class,
        basePackages = {"com.novunda.platform.repositories"})
@EnableTransactionManagement
@PropertySource(value = {"classpath:application.properties"})
public class AppConfig {

    private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);

    @Autowired
    private Environment env;

    @Value("${init-db:false}")
    private String initDatabase;

    @Bean
    public DataSourceInitializer dataSourceInitializer() {
        DataSourceInitializer dsInitializer = new DataSourceInitializer();
        dsInitializer.setDataSource(dataSource());
        String scripts = env.getProperty("scripts", "");
        logger.debug("Init-db :{}, scripts: {}", initDatabase, scripts);
        if (Boolean.parseBoolean(initDatabase) && StringUtils.hasLength(scripts)) {
            ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
            String[] files = scripts.split(",");
            for (String file : files) {
                databasePopulator.addScript(new ClassPathResource(file));
            }
            dsInitializer.setDatabasePopulator(databasePopulator);
        }

        return dsInitializer;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        EntityManagerFactory factory = entityManagerFactory().getObject();
        return new JpaTransactionManager(factory);
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource());

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(Boolean.TRUE);
        vendorAdapter.setShowSql(Boolean.TRUE);

        factory.setDataSource(dataSource());
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setPackagesToScan("com.novunda.platform.entities");

        Properties jpaProperties = new Properties();
        jpaProperties.put("hibernate.cache.region.factory_class",
                env.getProperty("hibernate.cache.region.factory_class"));
        jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
        jpaProperties.put("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
        jpaProperties.put("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
        jpaProperties.put("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
        jpaProperties.put("org.hibernate.cache.ehcache.configurationResourceName", env.getProperty("org.hibernate.cache.ehcache.configurationResourceName"));
        jpaProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
        factory.setJpaProperties(jpaProperties);


        factory.afterPropertiesSet();
        factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
        return factory;
    }

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

    @Bean
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setJdbcUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        dataSource.setMaximumPoolSize(95);
        dataSource.setMinimumIdle(10);
        dataSource.setConnectionTimeout(20000);
        dataSource.setValidationTimeout(10000);
        dataSource.setConnectionTestQuery("select 1");
        return dataSource;
    }

    @Bean(name = "validator")
    public Validator localValidator() {
        LocalValidatorFactoryBean localValidator = new LocalValidatorFactoryBean();
        localValidator.setProviderClass(HibernateValidator.class);
        localValidator.setValidationMessageSource(bundleMessageSource());
        return new LocalValidatorFactoryBean();
    }

    @Bean
    public ReloadableResourceBundleMessageSource bundleMessageSource() {
        ReloadableResourceBundleMessageSource bundleMessageSource = new ReloadableResourceBundleMessageSource();
        bundleMessageSource.setBasenames("validationMessages");
        bundleMessageSource.setDefaultEncoding("UTF-8");
        bundleMessageSource.setCacheSeconds(60);
        bundleMessageSource.setUseCodeAsDefaultMessage(false);
        return bundleMessageSource;
    }

    @Bean
    public EhCacheManagerFactoryBean getEhCacheFactory() {

        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("cache/ehcache-local.xml"));
        cacheManagerFactoryBean.setShared(false);
        return cacheManagerFactoryBean;
    }

    @Bean
    public CacheManager getEhCacheManager() {
        return new EhCacheCacheManager(getEhCacheFactory().getObject());
    }

}
